//! TestCase step implementation

use crate::error::{Error, Result};
use crate::models::{
    config::StepConfig,
    step::{IStep, StepType, StepResult, StepContext},
    Value, ValueMap,
};
use async_trait::async_trait;
use serde::{Deserialize, Serialize};
use std::path::PathBuf;
use std::time::Instant;
use uuid::Uuid;

/// TestCase reference configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TestCaseConfig {
    /// Path to the test case file
    pub path: String,

    /// Variables to export from the test case execution
    pub export: Option<Vec<String>>,

    /// Variables to pass to the test case
    pub variables: Option<ValueMap>,

    /// Whether to continue on failure
    pub continue_on_failure: Option<bool>,
}

/// TestCase step implementation
#[derive(Debug, Clone)]
pub struct TestCaseStep {
    id: Uuid,
    config: StepConfig,
    testcase_config: TestCaseConfig,
}

impl TestCaseStep {
    /// Create a new TestCase step
    pub fn new(config: StepConfig, testcase_config: TestCaseConfig) -> Self {
        Self {
            id: Uuid::new_v4(),
            config,
            testcase_config,
        }
    }

    /// Create a TestCase step with path
    pub fn with_path(testcase_path: String) -> Self {
        let config = StepConfig {
            step_name: format!("TestCase: {}", testcase_path),
            ..Default::default()
        };

        let testcase_config = TestCaseConfig {
            path: testcase_path,
            export: None,
            variables: None,
            continue_on_failure: None,
        };

        Self::new(config, testcase_config)
    }

    /// Create a TestCase step with path and export variables
    pub fn with_export(testcase_path: String, export_vars: Vec<String>) -> Self {
        let config = StepConfig {
            step_name: format!("TestCase: {} (export: {:?})", testcase_path, export_vars),
            ..Default::default()
        };

        let testcase_config = TestCaseConfig {
            path: testcase_path,
            export: Some(export_vars),
            variables: None,
            continue_on_failure: None,
        };

        Self::new(config, testcase_config)
    }

    /// Set variables to pass to the test case
    pub fn with_variables(mut self, variables: ValueMap) -> Self {
        self.testcase_config.variables = Some(variables);
        self
    }

    /// Set whether to continue on failure
    pub fn continue_on_failure(mut self, continue_on_failure: bool) -> Self {
        self.testcase_config.continue_on_failure = Some(continue_on_failure);
        self
    }

    /// Add export variables
    pub fn export(mut self, vars: Vec<String>) -> Self {
        self.testcase_config.export = Some(vars);
        self
    }
}

#[async_trait]
impl IStep for TestCaseStep {
    fn name(&self) -> &str {
        &self.config.step_name
    }

    fn step_type(&self) -> StepType {
        StepType::TestCase
    }

    fn config(&self) -> &StepConfig {
        &self.config
    }

    fn config_mut(&mut self) -> &mut StepConfig {
        &mut self.config
    }

    async fn execute(&self, context: &mut StepContext) -> Result<StepResult> {
        let start_time = Instant::now();
        let mut step_result = StepResult::new(self.id, self.name().to_string(), self.step_type());

        // Validate test case path
        let testcase_path = PathBuf::from(&self.testcase_config.path);
        if !testcase_path.exists() {
            step_result.success = false;
            step_result.error = Some(format!("Test case file not found: {}", self.testcase_config.path));
            return Ok(step_result);
        }

        // For now, we'll simulate test case execution since we don't have a full test case loader
        // In a real implementation, this would:
        // 1. Load the test case file (YAML/JSON)
        // 2. Parse it into a TestCaseDefinition
        // 3. Execute all steps in the test case
        // 4. Collect results and extract specified variables

        let mut response_data = ValueMap::new();
        response_data.insert("testcase_path".to_string(), Value::String(self.testcase_config.path.clone()));
        response_data.insert("status".to_string(), Value::String("simulated".to_string()));

        // Simulate passing variables to test case
        if let Some(ref variables) = self.testcase_config.variables {
            response_data.insert("passed_variables".to_string(),
                Value::Object(variables.clone().into_iter().collect()));
        }

        // Simulate test case execution results
        let simulated_results = ValueMap::from([
            ("total_steps".to_string(), Value::Number(5.into())),
            ("passed_steps".to_string(), Value::Number(5.into())),
            ("failed_steps".to_string(), Value::Number(0.into())),
            ("execution_time_ms".to_string(), Value::Number(1250.into())),
            ("final_status".to_string(), Value::String("passed".to_string())),
            ("extracted_session_id".to_string(), Value::String("session_abc123".to_string())),
            ("extracted_user_data".to_string(), Value::Object([
                ("id".to_string(), Value::Number(67890.into())),
                ("name".to_string(), Value::String("Test User".to_string())),
            ].into_iter().collect())),
        ]);

        response_data.insert("testcase_results".to_string(),
            Value::Object(simulated_results.clone().into_iter().collect()));

        // Export specified variables to context
        if let Some(ref export_vars) = self.testcase_config.export {
            let mut exported = ValueMap::new();

            for var_name in export_vars {
                // Try to find the variable in simulated results
                if let Some(value) = simulated_results.get(var_name) {
                    context.variables.insert(var_name.clone(), value.clone());
                    exported.insert(var_name.clone(), value.clone());
                } else {
                    // If not found, create a placeholder
                    let placeholder = Value::String(format!("exported_from_testcase_{}", var_name));
                    context.variables.insert(var_name.clone(), placeholder.clone());
                    exported.insert(var_name.clone(), placeholder);
                }
            }

            response_data.insert("exported_variables".to_string(),
                Value::Object(exported.into_iter().collect()));
        }

        // Determine success based on simulated results
        let continue_on_failure = self.testcase_config.continue_on_failure.unwrap_or(false);
        let testcase_passed = simulated_results.get("final_status")
            .and_then(|v| v.as_str())
            .map(|s| s == "passed")
            .unwrap_or(false);

        if testcase_passed || continue_on_failure {
            step_result.success = true;
        } else {
            step_result.success = false;
            step_result.error = Some(format!("Test case '{}' failed", self.testcase_config.path));
        }

        step_result.data = Some(Value::Object(response_data.into_iter().collect()));
        step_result.elapsed = start_time.elapsed().as_millis() as i64;

        Ok(step_result)
    }

    fn id(&self) -> Uuid {
        self.id
    }

    fn clone_step(&self) -> Box<dyn IStep> {
        Box::new(self.clone())
    }
}
